package Merlion.FRM.bean;

import Merlion.CRM.bean.SalesOrderProcessingModuleRemote;
import Merlion.FRM.entity.CreditProfileEntity;
import Merlion.CRM.entity.SalesOrderEntity;
import Merlion.FRM.entity.CustomerJournalEntity;
import Merlion.FRM.entity.InvoiceAndReceiptEntity;
import Merlion.FRM.entity.InvoiceLineEntryEntity;
import Merlion.FRM.entity.RefundEntity;
import Merlion.util.state.CustomerJournalState;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

@Stateless
public class PaymentBean implements PaymentBeanRemote {

    @PersistenceContext
    EntityManager em;
    @EJB
    private SalesOrderProcessingModuleRemote sop;

    public boolean performPayment(Long creditProfileId, Long invoiceId, Double payment) {
        System.out.println("**PaymentBean: performPayment()");

        CreditProfileEntity creditProfile = em.find(CreditProfileEntity.class, creditProfileId);
        InvoiceAndReceiptEntity invoiceAndReceipt = em.find(InvoiceAndReceiptEntity.class, invoiceId);
        Long salesOrderId = invoiceAndReceipt.getSalesOrderId();
        SalesOrderEntity salesOrder = em.find(SalesOrderEntity.class, salesOrderId);

        try {
            if (salesOrder.getPaymentMethod().equalsIgnoreCase("Cash")) {
                creditProfile.setBalancePayable(creditProfile.getBalancePayable() - payment);
                creditProfile.setCreditBalance(creditProfile.getCreditBalance() + payment);
                creditProfile.setPaymentAsCIA(creditProfile.getPaymentAsCIA() - payment);
                em.merge(creditProfile);
                em.flush();
            } else {
                creditProfile.setBalancePayable(creditProfile.getBalancePayable() - payment);
                creditProfile.setCreditBalance(creditProfile.getCreditBalance() + payment);
                em.merge(creditProfile);
                em.flush();
            }

            invoiceAndReceipt.setInvoiceStatus(true);
            System.out.println("**PaymentBean: Payment is successfully performed.");
            return true;
        } catch (Exception ex) {
            System.out.println("**PaymentBean: Fail to perform payment.");
            return false;
        }
    }

    public InvoiceAndReceiptEntity getInvoice(Long invoiceId) {
        System.out.println("**PaymentBean: getInvoice()");

        InvoiceAndReceiptEntity invoice = em.find(InvoiceAndReceiptEntity.class, invoiceId);
        return invoice;
    }

    public Long getSalesOrderId(Long invoiceId) {
        System.out.println("**PaymentBean: getSalesOrderId()");

        InvoiceAndReceiptEntity invoice = em.find(InvoiceAndReceiptEntity.class, invoiceId);
        Long salesOrderId = invoice.getSalesOrderId();
        return salesOrderId;
    }

    public Long getCreditProfileId(Long invoiceId) {
        System.out.println("**PaymentBean: getCreditProfileId()");

        InvoiceAndReceiptEntity invoice = em.find(InvoiceAndReceiptEntity.class, invoiceId);
        Long salesOrderId = invoice.getSalesOrderId();
        SalesOrderEntity salesOrder = em.find(SalesOrderEntity.class, salesOrderId);
        Long custId = salesOrder.getClient().getId();
        
        Query q = em.createQuery("SELECT c FROM CreditProfileEntity c WHERE c.customerId = :custId");
        q.setParameter("custId", custId);
        CreditProfileEntity cp = (CreditProfileEntity) q.getSingleResult();
        
        return cp.getCreditProfileId();
    }

    public String getCustomerName(Long invoiceId) {
        System.out.println("**PaymentBean: getCustomerName()");

        InvoiceAndReceiptEntity invoice = em.find(InvoiceAndReceiptEntity.class, invoiceId);
        Long salesOrderId = invoice.getSalesOrderId();
        SalesOrderEntity salesOrder = em.find(SalesOrderEntity.class, salesOrderId);
        String customerName = salesOrder.getClient().getName();
        
        return customerName;
    }

    public Collection<InvoiceLineEntryEntity> getLineEntry(Long invoiceId) {
        System.out.println("**PaymentBean: getLineEntry()");

        InvoiceAndReceiptEntity invoice = em.find(InvoiceAndReceiptEntity.class, invoiceId);
        Collection<InvoiceLineEntryEntity> lineEntry = invoice.getiLineEntry();
        lineEntry.size();
        
        return lineEntry;
    }

    public Double getTotalPrice(Long invoiceId) {
        System.out.println("**PaymentBean: getTotalPrice()");

        InvoiceAndReceiptEntity invoice = em.find(InvoiceAndReceiptEntity.class, invoiceId);
        Double totalPrice = invoice.getTotalPrice();
        
        return totalPrice;
    }

    public Double getAmountDue(Long invoiceId) {
        System.out.println("**PaymentBean: getAmountDue()");

        InvoiceAndReceiptEntity invoice = em.find(InvoiceAndReceiptEntity.class, invoiceId);
        Double amtDue = invoice.getAmountDue();
        
        return amtDue;
    }

    public Double getDiscountRate(Long invoiceId) {
        System.out.println("**PaymentBean: getDiscountRate()");

        InvoiceAndReceiptEntity invoice = em.find(InvoiceAndReceiptEntity.class, invoiceId);
        Double dscRate = invoice.getDiscountRate();
        
        return dscRate;
    }

    public Date getIssueDate(Long invoiceId) {
        System.out.println("**PaymentBean: getIssueDate()");

        InvoiceAndReceiptEntity invoice = em.find(InvoiceAndReceiptEntity.class, invoiceId);
        Date issueDate = invoice.getIssueDate();
        
        return issueDate;
    }

    public Date getInvoiceDueDate(Long invoiceId) {
        System.out.println("**PaymentBean: getInvoiceDueDate()");

        InvoiceAndReceiptEntity invoice = em.find(InvoiceAndReceiptEntity.class, invoiceId);
        Date dueDate = invoice.getInvoiceDueDate();
        
        return dueDate;
    }

    public int getCreditTerm(Long invoiceId) {
        System.out.println("**PaymentBean: getCreditTerm()");

        InvoiceAndReceiptEntity invoice = em.find(InvoiceAndReceiptEntity.class, invoiceId);
        int creditTerm = invoice.getCreditTerm();
        
        return creditTerm;
    }

    public int getDiscountedCreditTerm(Long invoiceId) {
        System.out.println("**PaymentBean: getDiscountedCreditTerm()");

        InvoiceAndReceiptEntity invoice = em.find(InvoiceAndReceiptEntity.class, invoiceId);
        int dCreditTerm = invoice.getDiscountedCreditTerm();
        
        return dCreditTerm;
    }

    public Long recordPaymentJournal(Long invoiceId, Double payment) {
        System.out.println("**PaymentBean : recordPaymentJournal()");
        
        InvoiceAndReceiptEntity invoice = em.find(InvoiceAndReceiptEntity.class, invoiceId);
        Long salesOrderId = invoice.getSalesOrderId();
        SalesOrderEntity soe = em.find(SalesOrderEntity.class, salesOrderId);
        Long customerId = soe.getClient().getId();

        try {
            CustomerJournalEntity journal = new CustomerJournalEntity();
            journal.setInvoiceId(invoiceId);
            journal.setCustomerId(customerId);
            journal.setSalesOrderId(salesOrderId);
            journal.setPaymentAmount(payment);
            journal.setDiscountFromCreditTerm(invoice.getDiscountRate());
            journal.setRemarks("N/A");
            em.persist(journal);
            em.flush();

            Long customerJournalId = journal.getCustomerJournalId();
            
            return customerJournalId;
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    //CRM will call this method when requesting for refund
    public Long createRefund(Long customerId, Long salesOrderId) {
        System.out.println("**PaymentBean : createRefund() ");

        RefundEntity refund = new RefundEntity();
        refund.setSalesOrderId(salesOrderId);
        refund.setCustomerId(customerId);
        refund.setRefundStatus(false);
        em.persist(refund);
        em.flush();

        Long refundId = refund.getRefundId();
        
        return refundId;
    }

    public List<RefundEntity> viewAllRefund() {
        System.out.println("**PaymentBean : viewAllRefund()");

        Query q = em.createQuery("SELECT i FROM RefundEntity i");
        List<RefundEntity> reStateList = new ArrayList<RefundEntity>();

        for (Object o : q.getResultList()) {
            RefundEntity i = (RefundEntity) o;
            if (i.isRefundStatus() == false) {
                RefundEntity re = new RefundEntity();
                re.setCustomerId(i.getCustomerId());
                re.setRefundId(i.getRefundId());
                re.setSalesOrderId(i.getSalesOrderId());
                re.setRefundStatus(i.isRefundStatus());
                reStateList.add(re);
            }
        }
        return reStateList;
    }

    public List<CustomerJournalState> viewAllPayment() {
        System.out.println("**PaymentBean : viewAllPayment()");

        Query q = em.createQuery("SELECT i FROM CustomerJournalEntity i");
        List<CustomerJournalState> stateList = new ArrayList<CustomerJournalState>();

        for (Object o : q.getResultList()) {
            CustomerJournalEntity c = (CustomerJournalEntity) o;
            CustomerJournalState cpState = new CustomerJournalState(
                    c.getCustomerJournalId(), c.getInvoiceId(), c.getCustomerId(),
                    c.getSalesOrderId(), c.getPaymentAmount(), c.getDiscountFromCreditTerm(),
                    c.getRemarks());

            stateList.add(cpState);
        }
        return stateList;
    }

    public boolean refundCashInAdvance(Long customerId, Long salesOrderId) {
        System.out.println("**PaymentBean: refundCashInAdvance()");
        
        try {
            //get total price that will be refunded - full CIA
            SalesOrderEntity salesOrder = em.find(SalesOrderEntity.class, salesOrderId);
            Double totalPrice = salesOrder.getTotalPrice();

            Query query = em.createQuery("SELECT c FROM CreditProfileEntity c WHERE c.customerId = :customerId");
            query.setParameter("customerId", customerId);
            CreditProfileEntity creditProfile = (CreditProfileEntity) query.getSingleResult();
            Double oldCIA = creditProfile.getPaymentAsCIA();
            Double newCIA = oldCIA - totalPrice;

            System.out.println("**PaymentBean :" + totalPrice + " will be refunded.");

            creditProfile.setBalancePayable(creditProfile.getBalancePayable() - totalPrice);
            creditProfile.setPaymentAsCIA(newCIA);
            creditProfile.setCreditBalance(creditProfile.getCreditBalance() + totalPrice);
            em.merge(creditProfile);
            em.flush();
            System.out.println("**PaymentBean : Cash in advance has been successfully refunded.");

            query = em.createQuery("SELECT r FROM RefundEntity r WHERE r.salesOrderId = :salesOrderId");
            query.setParameter("salesOrderId", salesOrderId);

            RefundEntity refundEntity = (RefundEntity) query.getSingleResult();
            refundEntity.setRefundStatus(true); //true = refunded
        } catch (NoResultException e) {
            System.out.println("**PaymentBean: Refund entity cannot be found because " + e.getMessage());
        }
        return true;
    }

    public Double getFinalAmt(Long invoiceId) {
        System.out.println("**PaymentBean: getFinalAmt()");

        Calendar calendar = Calendar.getInstance();
        Date dateToday = calendar.getTime();

        InvoiceAndReceiptEntity invoice = getInvoice(invoiceId);
        Date invoiceDueDate = invoice.getInvoiceDueDate();

        if (dateToday.after(invoiceDueDate)) {
            return invoice.getTotalPrice();
        } else if (dateToday.before(invoiceDueDate)) {
            return invoice.getAmountDue();
        } else {
            return invoice.getAmountDue();
        }
    }

    public void editJournal(Long customerJournalId, String remarks) {
        System.out.println("**PaymentBean: editJournal()");
        CustomerJournalEntity journal = em.find(CustomerJournalEntity.class, customerJournalId);

        try {
            if (journal != null) {
                journal.setRemarks(remarks);
                em.merge(journal);
                em.flush();

                System.out.println("**PaymentBean: Customer Journal ID: " + journal.getCustomerJournalId() + " successfully edited.");
            } else {
                System.out.println("**PaymentBean: Payment Record does not exist.");
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}